import { extend } from 'umi-request';
import aes from 'crypto-js/aes';
import { notification, message } from 'antd';
import CryptoJS from 'crypto-js';
import router from 'umi/router';
import md5 from 'crypto-js/md5';
import cookie from 'js-cookie';
import isEmpty from 'lodash/isEmpty';
import apiKey from '@/utils/key';
import secret from '@/utils/secret';
import { clearLoginStates } from '@/utils/utils';
import { encryptionRequest, logRequest } from '../../config/config';
import routes from '../../config/router.config';

// 不监控
const excludePaths = [
  '/api/oa/v1/test/heartbeat',
  '/api/user/v1/account/login',
];
// 不打印
const excludeLogPaths = ['/api/oa/v1/test/heartbeat'];

const getDecrypt = (params, secrets) =>
  JSON.parse(
    aes
      .decrypt(params, CryptoJS.enc.Utf8.parse(secrets), {
        iv: CryptoJS.enc.Utf8.parse(secrets),
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7,
      })
      .toString(CryptoJS.enc.Utf8)
  );
const utils = {
  getSign: (timeStamp, apiKeys, params) =>
    md5(`${timeStamp}${apiKeys}${params}`).toString().toUpperCase(),
  encrypt: (data, secrets) =>
    aes
      .encrypt(JSON.stringify(data), CryptoJS.enc.Utf8.parse(secrets), {
        iv: CryptoJS.enc.Utf8.parse(secrets),
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7,
      })
      .toString(),
  decrypt: (params, secrets, encryption = true) => {
    // encryption === false 不去解密响应报文
    if (typeof params !== 'string' || encryption === false) {
      return params;
    }
    try {
      return params && !isEmpty(params) ? getDecrypt(params, secrets) : params;
    } catch (e) {
      console.error(e);
      return params;
    }
  },
};

const codeMessage = {
  200: '服务器成功返回请求的数据。',
  201: '新建或修改数据成功。',
  202: '一个请求已经进入后台排队（异步任务）。',
  204: '删除数据成功。',
  400: '发出的请求有错误，服务器没有进行新建或修改数据的操作。',
  401: '用户没有权限（令牌、用户名、密码错误）。',
  403: '用户得到授权，但是访问是被禁止的。',
  404: '发出的请求针对的是不存在的记录，服务器没有进行操作。',
  406: '请求的格式不可得。',
  410: '请求的资源被永久删除，且不会再得到的。',
  422: '当创建一个对象时，发生一个验证错误。',
  500: '服务器发生错误，请检查服务器。',
  502: '网关错误。',
  503: '服务不可用，服务器暂时过载或维护。',
  504: '网关超时。',
};

/**
 * 异常处理程序
 */
const errorHandler = (error) => {
  const { response = {} } = error;
  const errortext = codeMessage[response.status] || response.statusText;
  const { status, url } = response;

  if (status === 401) {
    clearLoginStates();
    router.push('/user/login');
    return;
  }
  if (process.env.NODE_ENV !== 'production') {
    notification.error({
      message: `请求错误 ${status}: ${url}`,
      description: errortext,
    });
  }
};

let cur = {};
const findRoute = (path, routess) => {
  routess.forEach((route) => {
    if (route.path === path) {
      cur = route;
    } else if (route.routes) {
      findRoute(path, route.routes);
    }
  });
};

const token = cookie.get('Authorization');
const user = localStorage.getItem('user');
const { pathname } = window.location;
findRoute(pathname, routes);

// 如果路由有配置 static，则不需要登陆
if (!cur.static) {
  if (token) {
    if (user && JSON.parse(user)) {
      // const headers = {};
      // headers.Authorization = token;
    } else {
      clearLoginStates();
      router.push('/user/login');
    }
  } else if (!window.location.pathname.includes('login')) {
    clearLoginStates();
    router.push('/user/login');
  }
}

/**
 * 配置request请求时的默认参数
 */
const request = extend({
  errorHandler, // 默认错误处理
  credentials: 'include', // 默认请求是否带上cookie
});

message.config({
  maxCount: 1,
});

// request拦截器, 进行加密，可以请求时添加 encryption = false 绕过加密
request.interceptors.request.use((url, options) => {
  const now = +new Date();
  const {
    encryption = true,
    isFile = false,
    data = {},
    headers,
    ...rest
  } = options;
  const params = utils.encrypt(data, secret);
  const getRequestData = () => {
    let requestData;
    if (encryption && encryptionRequest) {
      requestData = {
        params,
        sign: utils.getSign(now, apiKey, params),
        timeStamp: now,
      };
    } else if (isFile) {
      requestData = JSON.stringify(data);
    } else {
      requestData = { params: JSON.stringify(data), timeStamp: now };
    }
    return requestData;
  };

  if (logRequest && !excludeLogPaths.includes(url)) {
    console.info(
      `%c 请求路径：${url}，请求类型：${(
        options.method || 'GET'
      ).toUpperCase()}，请求参数：`,
      'color: #ff6600',
      options.data || options.params
    );
    const newUser = localStorage.getItem('user')
      ? JSON.parse(localStorage.getItem('user'))
      : {};
    // isFile 如果请求的是文件流 服务端要求body不包裹在params里😠
    const tmpOptions = {
      data: getRequestData(),
      interceptors: true,
      headers: {
        ...headers,
        Authorization: cookie.get('Authorization'),
        uc: newUser.userCode || '',
      },
      ...rest,
    };
    // 防止 IE11 下 GET 方法的请求缓存
    // if (rest.method === 'get') {
    if (!excludePaths.includes(url)) {
      tmpOptions.params = {
        ...rest.params,
        t: now, // 时间戳
        path: url, // 请求路径
      };
    }
    // }
    return {
      url,
      options: tmpOptions,
    };
  }
  return null;
});

// response拦截器, 处理response
request.interceptors.response.use(async (response, options) => {
  const newUser = localStorage.getItem('user')
    ? JSON.parse(localStorage.getItem('user'))
    : {};
  const startTime = options.params.t;
  const endTime = +new Date();
  const spendTime = endTime - startTime;
  // 心跳不监听
  if (options.params.path && !excludePaths.includes(response.url)) {
    fetch(
      `/api/oa/v1/ntrt?uc=${
        newUser.userCode || ''
      }&spendTime=${spendTime}&url=${decodeURI(
        options.params.path
      )}&startTime=${startTime}&endTime=${endTime}`,
      {
        method: 'GET',
        headers: {
          ...response.headers,
          Authorization: cookie.get('Authorization'),
        },
      }
    ).then((res) => {
      if (res.status !== 200) {
        // console.error('监听失败！');
      }
    });
  }
  return response;
});

async function requestWrap(...params) {
  if (params[1] && params[1].encryption === false && !encryptionRequest) {
    return request(...params);
  }
  const pa = {
    ...params[1],
    getResponse: true,
  };
  const result = await request(params[0], pa);
  // 截取 20008 状态码
  if (result && result.data && result.data.code === 20008) {
    clearLoginStates();
    message.error(result.data.msg);
    router.replace('/user/login');
  }

  // 截取 30012 状态码
  if (result && result.data && result.data.code === 30012) {
    message.error(result.data.msg);
    router.push('/personal/portal/portal');
  }

  // 非正常返回，console一下
  if (result && result.data && result.data.code !== 10000) {
    console.error(result);
  }

  const getResponse = params[1] && params[1].getResponse;
  const encryption = params[1] && params[1].encryption;
  const newResponse =
    result && result.response && result.response.headers.get('Authorization');

  // 如果token更新，则重置token
  if (newResponse) {
    cookie.set(
      'Authorization',
      result.response.headers.get('Authorization') || '',
      {
        expires: 30,
      }
    );
  }
  if (logRequest && !excludeLogPaths.includes(params[0])) {
    console.info(
      `%c 请求路径：${params[0]}，返回数据：`,
      'color: green',
      utils.decrypt(result && result.data && result.data.data, secret)
    );
  }
  if (getResponse && encryption === false) {
    return Promise.resolve({
      ...result,
    });
  }
  if (getResponse) {
    return Promise.resolve({
      ...result,
      data: {
        ...result.data,
        data: utils.decrypt(result.data.data, secret, encryption),
      },
    });
  }
  return Promise.resolve({
    ...(result && result.data),
    data: utils.decrypt(result && result.data && result.data.data, secret),
  });
}
Object.keys(request).forEach((key) => {
  requestWrap[key] = request[key];
});

export default requestWrap;
